﻿using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;

namespace ChessKit.Logic
{
	public partial class Board
	{
		private byte[] Squares { get { return ProtoBoard.Squares; } }

		public GameState State { get; private set; }
		public ulong PinsMap { get; private set; }

		public int CountWhiteAttackers(Square ofSquare) { return 2; }
		public int CountBlackAttackers(Square ofSquare) { return 2; }
		public MoveHints ValidateMove(Move move)
		{
			if (this[move.From] == 0) return MoveHints.EmptyCell;
			if (!this[move.From].Is(SideOnMove)) return (MoveHints)this[move.From] | MoveHints.WrongSideToMove;
			if (this[move.To].Is(SideOnMove)) return (MoveHints)this[move.From] | MoveHints.ToOccupiedCell;
			var hints = ValidateMove(this[move.From], move.From, move.To, this[move.To], CastlingAvailability);
			if (this[move.To] != Piece.EmptyCell) hints |= MoveHints.Capture;
			if ((hints & MoveHints.AllErrors) != 0) return hints;
			if ((hints & MoveHints.EnPassant) != 0 && EnPassantFile != move.To % 16)
				return hints | MoveHints.HasNoEnPassant;

			// BUG: It seems like there's no point in "just validating" move, because
			// we cannot check if it is move-to-check without actually setting the position up
			var board = new Board(new ProtoBoard(this, move, hints, PieceType.Queen), 10);
			if (board.IsUnderCheck(SideOnMove)) return hints | MoveHints.MoveToCheck;
			return hints;
		}
		public BoardStates ValidateBoard(Move move) { return 0; }
		public IEnumerable<HintedMove> GetLegalMovesFrom(Square square)
		{
			return LegalMoves.Where(m => m.From == square);
		}
		public IEnumerable<HintedMove> GetLegalMovesTo(Square square)
		{
			return LegalMoves.Where(m => m.To == square);
		}
		public MadeMove MakeMove(Move move, PieceType promotion = PieceType.Queen)
		{
			var validateMove = ValidateMove(move);
			return new MadeMove(this,
				new HintedMove(this, move, validateMove), promotion,
				new ProtoBoard(this, move, validateMove, promotion));
		}

		public HintedMove ParseMove(string sanString) { return new HintedMove(); }
		public ReadOnlyCollection<HintedMove> LegalMoves { get; private set; }
		public bool IsThreefoldRepetition()
		{
			//      if (ProtoBoard.MoveBefore)
			return false;
		}

		#region ' From ProtoBoard '

		public ProtoBoard ProtoBoard { get; private set; }
		public int MoveNumber { get { return ProtoBoard.MoveNumber; } }
		public PieceColor SideOnMove { get { return ProtoBoard.SideOnMove; } }
		public override string ToString() { return ProtoBoard.ToString(); }
		public string Print() { return ProtoBoard.Print(); }
		public int? EnPassantFile { get { return ProtoBoard.EnPassantFile; } }
		public int FiftyMoveClock { get { return ProtoBoard.FiftyMoveClock; } }
		public CastlingAvailability CastlingAvailability { get { return ProtoBoard.CastlingAvailability; } }
		public Piece this[Square index] { get { return ProtoBoard[index]; } }

		#endregion

		/// <summary>
		/// 
		/// </summary>
		/// <param name="origin"></param>
		/// <param name="dummy">BUG: WTF?</param>
		internal Board(ProtoBoard origin, int dummy)
		{
			ProtoBoard = origin;

			PinsMap = 0xFFFFFFFFFFFFFFFF; // All pinned

		}
		public Board(ProtoBoard origin)
		{
			ProtoBoard = origin;

			PinsMap = 0xFFFFFFFFFFFFFFFF; // All pinned
			if (IsUnderCheck(SideOnMove))
			{
				State = GameState.Check;
			}
			else
			{
				PinsMap = SideOnMove == PieceColor.White
				  ? BuildWhitePinMap(WhiteKingPosition)
				  : BuildBlackPinMap(BlackKingPosition);
			}

			LegalMoves = new ReadOnlyCollection<HintedMove>(GetLegalMoves());

			if (LegalMoves.Count == 0)
			{
				State = State == GameState.Check
						  ? GameState.Mate
						  : GameState.Stalemate;
			}
		}

		private List<HintedMove> GetLegalMoves()
		{
			var res = new List<HintedMove>(50);
			var sideOnMove = SideOnMove;
			foreach (var moveFrom in Square.All)
			{
				var piece = Squares[moveFrom];
				if (((MoveHints)piece & (MoveHints)sideOnMove) == 0) continue;
				GenerateMoves((Piece)piece, moveFrom, EnPassantFile, CastlingAvailability, res);
			}
			return res;
		}

		public bool TryGetLegalMove(Move moveToFind, out HintedMove result)
		{
			for (int i = 0; i < LegalMoves.Count; i++)
			{
				if (LegalMoves[i].Move == moveToFind)
				{
					result = LegalMoves[i];
					return true;
				}
			}
			result = new HintedMove();
			return false;
		}
		public bool TryMakeMove(Move moveToFind, out MadeMove result)
		{
			HintedMove legal;
			if (!TryGetLegalMove(moveToFind, out legal))
			{
				result = new MadeMove();
				return false;
			}
			result = new MadeMove(this, legal, PieceType.Queen);
			return true;
		}
	}
}